#include "wx/wxprec.h"
#include "wx/utils.h"
#include "wx/apptrait.h"
#include "wx/process.h"
#include "wx/thread.h"
#include "wx/wfstream.h"
#include "wx/unix/execute.h"
#include "wx/unix/private.h"
#include <pwd.h>

#ifdef HAVE_SYS_SELECT_H
#   include <sys/select.h>
#endif

#define HAS_PIPE_INPUT_STREAM (wxUSE_STREAMS && wxUSE_FILE)

#if HAS_PIPE_INPUT_STREAM

#define _WX_USED_BY_WXEXECUTE_
#include "../common/execcmn.cpp"

#endif // HAS_PIPE_INPUT_STREAM

#if wxUSE_BASE

#if defined(__MWERKS__) && defined(__MACH__)
#ifndef WXWIN_OS_DESCRIPTION
#define WXWIN_OS_DESCRIPTION "MacOS X"
#endif
#ifndef HAVE_NANOSLEEP
#define HAVE_NANOSLEEP
#endif
#ifndef HAVE_UNAME
#define HAVE_UNAME
#endif

#ifdef __MSL__
#undef wxUSE_ON_FATAL_EXCEPTION
#define wxUSE_ON_FATAL_EXCEPTION 0
#endif
#endif

#ifdef HAVE_STATFS
#ifdef __BSD__
#include <sys/param.h>
#include <sys/mount.h>
#else // !__BSD__
#include <sys/vfs.h>
#endif // __BSD__/!__BSD__

#define wxStatfs statfs

#ifndef HAVE_STATFS_DECL
extern "C" int statfs( const char *path, struct statfs *buf );
#endif
#endif // HAVE_STATFS

#ifdef HAVE_STATVFS
#include <sys/statvfs.h>

#define wxStatfs statvfs
#endif // HAVE_STATVFS

#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
// WX_STATFS_T is detected by configure
#define wxStatfs_t WX_STATFS_T
#endif

// SGI signal.h defines signal handler arguments differently depending on
// whether _LANGUAGE_C_PLUS_PLUS is set or not - do set it
#if defined(__SGI__) && !defined(_LANGUAGE_C_PLUS_PLUS)
#define _LANGUAGE_C_PLUS_PLUS 1
#endif // SGI hack

#include <stdarg.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <fcntl.h>          // for O_WRONLY and friends
#include <time.h>           // nanosleep() and/or usleep()
#include <ctype.h>          // isspace()
#include <sys/time.h>       // needed for FD_SETSIZE

#ifdef HAVE_UNAME
#include <sys/utsname.h> // for uname()
#endif // HAVE_UNAME

// Used by wxGetFreeMemory().
#ifdef __SGI__
#include <sys/sysmp.h>
#include <sys/sysinfo.h>   // for SAGET and MINFO structures
#endif

#if !defined(HAVE_USLEEP) && \
((defined(__SUN__) && !defined(__SunOs_5_6) && \
  !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
 defined(__osf__) || defined(__EMX__))
extern "C"
{
  #ifdef __EMX__
  /* I copied this from the XFree86 diffs. AV. */
#define INCL_DOSPROCESS
#include <os2.h>
  inline void usleep( unsigned long delay ) {
    DosSleep( delay ? ( delay / 1000l ) : 1l );
  }
  #else // Unix
  int usleep( unsigned int usec );
  #endif // __EMX__/Unix
};

#define HAVE_USLEEP 1
#endif // Unices without usleep()

void wxSleep( int nSecs ) {
  sleep( nSecs );
}

void wxMicroSleep( unsigned long microseconds ) {
  #if defined(HAVE_NANOSLEEP)
  timespec tmReq;
  tmReq.tv_sec = ( time_t )( microseconds / 1000000 );
  tmReq.tv_nsec = ( microseconds % 1000000 ) * 1000;
  // we're not interested in remaining time nor in return value
  ( void )nanosleep( &tmReq, ( timespec * )NULL );
  #elif defined(HAVE_USLEEP)
  #if defined(__SUN__) && wxUSE_THREADS
#error "usleep() cannot be used in MT programs under Solaris."
  #endif // Sun
  usleep( microseconds );
  #elif defined(HAVE_SLEEP)
  // under BeOS sleep() takes seconds (what about other platforms, if any?)
  sleep( microseconds * 1000000 );
  #else // !sleep function
#error "usleep() or nanosleep() function required for wxMicroSleep"
  #endif // sleep function
}

void wxMilliSleep( unsigned long milliseconds ) {
  wxMicroSleep( milliseconds * 1000 );
}

int wxKill( long pid, wxSignal sig, wxKillError *rc, int flags ) {
  int err = kill( ( pid_t )( flags & wxKILL_CHILDREN ) ? -pid : pid, ( int )sig );
  if( rc ) {
    switch( err ? errno : 0 ) {
      case 0:
        *rc = wxKILL_OK;
        break;
      case EINVAL:
        *rc = wxKILL_BAD_SIGNAL;
        break;
      case EPERM:
        *rc = wxKILL_ACCESS_DENIED;
        break;
      case ESRCH:
        *rc = wxKILL_NO_PROCESS;
        break;
      default:
        // this goes against Unix98 docs so log it
        wxLogDebug( _T( "unexpected kill(2) return value %d" ), err );
        // something else...
        *rc = wxKILL_ERROR;
    }
  }
  return err;
}

#define WXEXECUTE_NARGS   127

long wxExecute( const wxString& command, int flags, wxProcess *process ) {
  wxCHECK_MSG( !command.empty(), 0, wxT( "can't exec empty command" ) );
  wxLogTrace( wxT( "exec" ), wxT( "Executing \"%s\"" ), command.c_str() );
  #if wxUSE_THREADS
  wxASSERT_MSG( wxThread::IsMain(),
                _T( "wxExecute() can be called only from the main thread" ) );
  #endif // wxUSE_THREADS
  int argc = 0;
  wxChar *argv[WXEXECUTE_NARGS];
  wxString argument;
  const wxChar *cptr = command.c_str();
  wxChar quotechar = wxT( '\0' ); // is arg quoted?
  bool escaped = false;
  // split the command line in arguments
  do {
    argument = wxEmptyString;
    quotechar = wxT( '\0' );
    // eat leading whitespace:
    while( wxIsspace( *cptr ) ) {
      cptr++;
    }
    if( *cptr == wxT( '\'' ) || *cptr == wxT( '"' ) ) {
      quotechar = *cptr++;
    }
    do {
      if( *cptr == wxT( '\\' ) && ! escaped ) {
        escaped = true;
        cptr++;
        continue;
      }
      // all other characters:
      argument += *cptr++;
      escaped = false;
      // have we reached the end of the argument?
      if( ( *cptr == quotechar && ! escaped )
          || ( quotechar == wxT( '\0' ) && wxIsspace( *cptr ) )
          || *cptr == wxT( '\0' ) ) {
        wxASSERT_MSG( argc < WXEXECUTE_NARGS,
                      wxT( "too many arguments in wxExecute" ) );
        argv[argc] = new wxChar[argument.length() + 1];
        wxStrcpy( argv[argc], argument.c_str() );
        argc++;
        // if not at end of buffer, swallow last character:
        if( *cptr ) {
          cptr++;
        }
        break; // done with this one, start over
      }
    } while( *cptr );
  } while( *cptr );
  argv[argc] = NULL;
  long lRc;
  // do execute the command
  lRc = wxExecute( argv, flags, process );
  // clean up
  argc = 0;
  while( argv[argc] ) {
    delete [] argv[argc++];
  }
  return lRc;
}

static wxString wxMakeShellCommand( const wxString& command ) {
  wxString cmd;
  if( !command ) {
    // just an interactive shell
    cmd = _T( "xterm" );
  } else {
    // execute command in a shell
    cmd << _T( "/bin/sh -c '" ) << command << _T( '\'' );
  }
  return cmd;
}

bool wxShell( const wxString& command ) {
  return wxExecute( wxMakeShellCommand( command ), wxEXEC_SYNC ) == 0;
}

bool wxShell( const wxString& command, wxArrayString& output ) {
  wxCHECK_MSG( !command.empty(), false, _T( "can't exec shell non interactively" ) );
  return wxExecute( wxMakeShellCommand( command ), output );
}

// Shutdown or reboot the PC
bool wxShutdown( wxShutdownFlags wFlags ) {
  wxChar level;
  switch( wFlags ) {
    case wxSHUTDOWN_POWEROFF:
      level = _T( '0' );
      break;
    case wxSHUTDOWN_REBOOT:
      level = _T( '6' );
      break;
    default:
      wxFAIL_MSG( _T( "unknown wxShutdown() flag" ) );
      return false;
  }
  return system( wxString::Format( _T( "init %c" ), level ).mb_str() ) == 0;
}

#if HAS_PIPE_INPUT_STREAM

bool wxPipeInputStream::CanRead() const {
  if( m_lasterror == wxSTREAM_EOF ) {
    return false;
  }
  // check if there is any input available
  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  const int fd = m_file->fd();
  fd_set readfds;
  wxFD_ZERO( &readfds );
  wxFD_SET( fd, &readfds );
  switch( select( fd + 1, &readfds, NULL, NULL, &tv ) ) {
    case -1:
      wxLogSysError( _( "Impossible to get child process input" ) );
    // fall through
    case 0:
      return false;
    default:
      wxFAIL_MSG( _T( "unexpected select() return value" ) );
    // still fall through
    case 1:
      return !Eof();
  }
}

#endif // HAS_PIPE_INPUT_STREAM

long wxExecute( wxChar **argv, int flags, wxProcess *process ) {
#define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0)
  wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT( "can't exec empty command" ) );
  int mb_argc = 0;
  char *mb_argv[WXEXECUTE_NARGS];
  while( argv[mb_argc] ) {
    wxWX2MBbuf mb_arg = wxSafeConvertWX2MB( argv[mb_argc] );
    mb_argv[mb_argc] = strdup( mb_arg );
    mb_argc++;
  }
  mb_argv[mb_argc] = ( char * ) NULL;
  // this macro will free memory we used above
#define ARGS_CLEANUP                                 \
  for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
    free(mb_argv[mb_argc])
  wxConsoleAppTraits traitsConsole;
  wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
  if( !traits ) {
    traits = &traitsConsole;
  }
  wxExecuteData execData;
  execData.flags = flags;
  execData.process = process;
  // create pipes
  if( !traits->CreateEndProcessPipe( execData ) ) {
    wxLogError( _( "Failed to execute '%s'\n" ), *argv );
    ARGS_CLEANUP;
    return ERROR_RETURN_CODE;
  }
  // pipes for inter process communication
  wxPipe pipeIn,      // stdin
         pipeOut,     // stdout
         pipeErr;     // stderr
  if( process && process->IsRedirected() ) {
    if( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() ) {
      wxLogError( _( "Failed to execute '%s'\n" ), *argv );
      ARGS_CLEANUP;
      return ERROR_RETURN_CODE;
    }
  }
  pid_t pid = fork();
  if( pid == -1 ) {    // error?
    wxLogSysError( _( "Fork failed" ) );
    ARGS_CLEANUP;
    return ERROR_RETURN_CODE;
  } else if( pid == 0 ) { // we're in child
    if( !( flags & wxEXEC_SYNC ) ) {
      for( int fd = 0; fd < ( int )FD_SETSIZE; fd++ ) {
        if( fd == pipeIn[wxPipe::Read] || fd == pipeOut[wxPipe::Write]
            || fd == pipeErr[wxPipe::Write] || traits->IsWriteFDOfEndProcessPipe( execData, fd ) ) {
          continue;
        }
        if( fd != STDERR_FILENO ) {
          close( fd );
        }
      }
    }
    if( flags & wxEXEC_MAKE_GROUP_LEADER ) {
      setsid();
    }
    traits->DetachWriteFDOfEndProcessPipe( execData );
    if( pipeIn.IsOk() ) {
      if( dup2( pipeIn[wxPipe::Read], STDIN_FILENO ) == -1 ||
          dup2( pipeOut[wxPipe::Write], STDOUT_FILENO ) == -1 ||
          dup2( pipeErr[wxPipe::Write], STDERR_FILENO ) == -1 ) {
        wxLogSysError( _( "重定向子进程输入/输出失败" ) );
      }
      pipeIn.Close();
      pipeOut.Close();
      pipeErr.Close();
    }
    execvp( *mb_argv, mb_argv );
    fprintf( stderr, "execvp(" );
    for( char **ppc_ = mb_argv; *ppc_; ppc_++ ) {
      fprintf( stderr, "%s%s", ppc_ == mb_argv ? "" : ", ", *ppc_ );
    }
    fprintf( stderr, ") failed with error %d!\n", errno );
    // there is no return after successful exec()
    _exit( -1 );
    #if defined(__VMS) || defined(__INTEL_COMPILER)
    return 0;
    #endif
  } else { // we're in parent
    ARGS_CLEANUP;
    // save it for WaitForChild() use
    execData.pid = pid;
    // prepare for IO redirection
    #if HAS_PIPE_INPUT_STREAM
    // the input buffer bufOut is connected to stdout, this is why it is
    // called bufOut and not bufIn
    wxStreamTempInputBuffer bufOut,bufErr;
    #endif // HAS_PIPE_INPUT_STREAM
    if( process && process->IsRedirected() ) {
      #if HAS_PIPE_INPUT_STREAM
      wxOutputStream *inStream =
        new wxFileOutputStream( pipeIn.Detach( wxPipe::Write ) );
      wxPipeInputStream *outStream =
        new wxPipeInputStream( pipeOut.Detach( wxPipe::Read ) );
      wxPipeInputStream *errStream =
        new wxPipeInputStream( pipeErr.Detach( wxPipe::Read ) );
      process->SetPipeStreams( outStream, inStream, errStream );
      bufOut.Init( outStream );
      bufErr.Init( errStream );
      execData.bufOut = &bufOut;
      execData.bufErr = &bufErr;
      #endif // HAS_PIPE_INPUT_STREAM
    }
    if( pipeIn.IsOk() ) {
      pipeIn.Close();
      pipeOut.Close();
      pipeErr.Close();
    }
    return traits->WaitForChild( execData );
  }
  #if !defined(__VMS) && !defined(__INTEL_COMPILER)
  return ERROR_RETURN_CODE;
  #endif
}

#undef ERROR_RETURN_CODE
#undef ARGS_CLEANUP

const wxChar* wxGetHomeDir( wxString *home ) {
  *home = wxGetUserHome( wxEmptyString );
  wxString tmp;
  if( home->empty() ) {
    *home = wxT( "/" );
  }
  return home->c_str();
}

const wxMB2WXbuf wxGetUserHome( const wxString &user ) {
  struct passwd *who = ( struct passwd * ) NULL;
  if( !user ) {
    wxChar *ptr;
    if( ( ptr = wxGetenv( wxT( "HOME" ) ) ) != NULL ) {
      wxWCharBuffer buffer( ptr );
      return buffer;
    }
    if( ( ptr = wxGetenv( wxT( "USER" ) ) ) != NULL || ( ptr = wxGetenv( wxT( "LOGNAME" ) ) ) != NULL ) {
      who = getpwnam( wxSafeConvertWX2MB( ptr ) );
    }
    // We now make sure the the user exists!
    if( who == NULL ) {
      who = getpwuid( getuid() );
    }
  } else {
    who = getpwnam( user.mb_str() );
  }
  return wxSafeConvertMB2WX( who ? who->pw_dir : 0 );
}

static wxString wxGetCommandOutput( const wxString &cmd ) {
  FILE *f = popen( cmd.ToAscii(), "r" );
  if( !f ) {
    wxLogSysError( _T( "Executing \"%s\" failed" ), cmd.c_str() );
    return wxEmptyString;
  }
  wxString s;
  char buf[256];
  while( !feof( f ) ) {
    if( !fgets( buf, sizeof( buf ), f ) ) {
      break;
    }
    s += wxString::FromAscii( buf );
  }
  pclose( f );
  if( !s.empty() && s.Last() == _T( '\n' ) ) {
    s.RemoveLast();
  }
  return s;
}

static bool wxGetHostNameInternal( wxChar *buf, int sz ) {
  wxCHECK_MSG( buf, false, wxT( "NULL pointer in wxGetHostNameInternal" ) );
  *buf = wxT( '\0' );
  #if defined(HAVE_UNAME)
  struct utsname uts;
  bool ok = uname( &uts ) != -1;
  if( ok ) {
    wxStrncpy( buf, wxSafeConvertMB2WX( uts.nodename ), sz - 1 );
    buf[sz] = wxT( '\0' );
  }
  #elif defined(HAVE_GETHOSTNAME)
  char cbuf[sz];
  bool ok = gethostname( cbuf, sz ) != -1;
  if( ok ) {
    wxStrncpy( buf, wxSafeConvertMB2WX( cbuf ), sz - 1 );
    buf[sz] = wxT( '\0' );
  }
  #else // no uname, no gethostname
  wxFAIL_MSG( wxT( "不知道此计算机的主机名" ) );
  bool ok = false;
  #endif // uname/gethostname
  if( !ok ) {
    wxLogSysError( _( "无法获取主机名" ) );
  }
  return ok;
}

bool wxGetHostName( wxChar *buf, int sz ) {
  bool ok = wxGetHostNameInternal( buf, sz );
  if( ok ) {
    wxChar *dot = wxStrchr( buf, wxT( '.' ) );
    if( dot ) {
      // nuke it
      *dot = wxT( '\0' );
    }
  }
  return ok;
}

bool wxGetFullHostName( wxChar *buf, int sz ) {
  bool ok = wxGetHostNameInternal( buf, sz );
  if( ok ) {
    if( !wxStrchr( buf, wxT( '.' ) ) ) {
      struct hostent *host = gethostbyname( wxSafeConvertWX2MB( buf ) );
      if( !host ) {
        wxLogSysError( _( "无法获取正式主机名" ) );
        ok = false;
      } else {
        wxStrncpy( buf, wxSafeConvertMB2WX( host->h_name ), sz );
      }
    }
  }
  return ok;
}

bool wxGetUserId( wxChar *buf, int sz ) {
  struct passwd *who;
  *buf = wxT( '\0' );
  if( ( who = getpwuid( getuid() ) ) != NULL ) {
    wxStrncpy( buf, wxSafeConvertMB2WX( who->pw_name ), sz - 1 );
    return true;
  }
  return false;
}

bool wxGetUserName( wxChar *buf, int sz ) {
  #ifdef HAVE_PW_GECOS
  struct passwd *who;
  *buf = wxT( '\0' );
  if( ( who = getpwuid( getuid() ) ) != NULL ) {
    char *comma = strchr( who->pw_gecos, ',' );
    if( comma ) {
      *comma = '\0';
    } // cut off non-name comment fields
    wxStrncpy( buf, wxSafeConvertMB2WX( who->pw_gecos ), sz - 1 );
    return true;
  }
  return false;
  #else // !HAVE_PW_GECOS
  return wxGetUserId( buf, sz );
  #endif // HAVE_PW_GECOS/!HAVE_PW_GECOS
}

bool wxIsPlatform64Bit() {
  const wxString machine = wxGetCommandOutput( wxT( "uname -m" ) );
  return machine.Contains( wxT( "64" ) ) || machine.Contains( wxT( "alpha" ) );
}

wxOperatingSystemId wxGetOsVersion( int *verMaj, int *verMin ) {
  // get OS version
  int major, minor;
  wxString release = wxGetCommandOutput( wxT( "uname -r" ) );
  if( !release.empty() && wxSscanf( release, wxT( "%d.%d" ), &major, &minor ) != 2 ) {
    // unrecognized uname string format
    major =
      minor = -1;
  }
  if( verMaj ) {
    *verMaj = major;
  }
  if( verMin ) {
    *verMin = minor;
  }
  // try to understand which OS are we running
  wxString kernel = wxGetCommandOutput( wxT( "uname -s" ) );
  if( kernel.empty() ) {
    kernel = wxGetCommandOutput( wxT( "uname -o" ) );
  }
  if( kernel.empty() ) {
    return wxOS_UNKNOWN;
  }
  return wxPlatformInfo::GetOperatingSystemId( kernel );
}

wxString wxGetOsDescription() {
  return wxGetCommandOutput( wxT( "uname -s -r -m" ) );
}

unsigned long wxGetProcessId() {
  return ( unsigned long )getpid();
}

wxMemorySize wxGetFreeMemory() {
  FILE *fp = fopen( "/proc/meminfo", "r" );
  if( fp ) {
    long memFree = -1;
    char buf[1024];
    if( fgets( buf, WXSIZEOF( buf ), fp ) && fgets( buf, WXSIZEOF( buf ), fp ) ) {
      if( wxPlatformInfo().CheckOSVersion( 2, 6 ) ) {
        unsigned long cached, buffers;
        sscanf( buf, "MemFree: %ld", &memFree );
        fgets( buf, WXSIZEOF( buf ), fp );
        sscanf( buf, "Buffers: %lu", &buffers );
        fgets( buf, WXSIZEOF( buf ), fp );
        sscanf( buf, "Cached: %lu", &cached );
        memFree += buffers + cached;
        // values here are always expressed in kB and we want bytes
        memFree *= 1024;
      } else { // Linux 2.4 (or < 2.6, anyhow)
        long memTotal, memUsed;
        sscanf( buf, "Mem: %ld %ld %ld", &memTotal, &memUsed, &memFree );
      }
    }
    fclose( fp );
    return ( wxMemorySize )memFree;
  }
  return -1;
}

bool wxGetDiskSpace( const wxString& path, wxDiskspaceSize_t *pTotal, wxDiskspaceSize_t *pFree ) {
  #if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
  // the case to "char *" is needed for AIX 4.3
  wxStatfs_t fs;
  if( wxStatfs( ( char * )( const char* )path.fn_str(), &fs ) != 0 ) {
    wxLogSysError( wxT( "Failed to get file system statistics" ) );
    return false;
  }
  #ifdef HAVE_STATVFS
  wxDiskspaceSize_t blockSize = fs.f_frsize;
  #else // HAVE_STATFS
  wxDiskspaceSize_t blockSize = fs.f_bsize;
  #endif // HAVE_STATVFS/HAVE_STATFS
  if( pTotal ) {
    *pTotal = wxDiskspaceSize_t( fs.f_blocks ) * blockSize;
  }
  if( pFree ) {
    *pFree = wxDiskspaceSize_t( fs.f_bavail ) * blockSize;
  }
  return true;
  #else // !HAVE_STATFS && !HAVE_STATVFS
  return false;
  #endif // HAVE_STATFS
}

bool wxGetEnv( const wxString& var, wxString *value ) {
  wxChar *p = wxGetenv( var );
  if( !p ) {
    return false;
  }
  if( value ) {
    *value = p;
  }
  return true;
}

bool wxSetEnv( const wxString& variable, const wxChar *value ) {
  #if defined(HAVE_SETENV)
  if( !value ) {
    #ifdef HAVE_UNSETENV
    unsetenv( variable.mb_str() );
    return true;
    #else
    value = _T( "" );
    #endif
  }
  return setenv( variable.mb_str(), wxString( value ).mb_str(), 1 ) == 0;
  #elif defined(HAVE_PUTENV)
  wxString s = variable;
  if( value ) {
    s << _T( '=' ) << value;
  }
  // transform to ANSI
  const wxWX2MBbuf p = s.mb_str();
  // the string will be free()d by libc
  char *buf = ( char * )malloc( strlen( p ) + 1 );
  strcpy( buf, p );
  return putenv( buf ) == 0;
  #else // no way to set an env var
  return false;
  #endif
}

#if wxUSE_ON_FATAL_EXCEPTION

#include <signal.h>

extern "C" void wxFatalSignalHandler( wxTYPE_SA_HANDLER ) {
  if( wxTheApp ) {
    wxTheApp->OnFatalException();
  }
  abort();
}

bool wxHandleFatalExceptions( bool doit ) {
  // old sig handlers
  static bool s_savedHandlers = false;
  static struct sigaction s_handlerFPE, s_handlerILL, s_handlerBUS, s_handlerSEGV;
  bool ok = true;
  if( doit && !s_savedHandlers ) {
    struct sigaction act;
    memset( &act, 0, sizeof( act ) );
    act.sa_handler = wxFatalSignalHandler;
    sigemptyset( &act.sa_mask );
    act.sa_flags = 0;
    ok &= sigaction( SIGFPE, &act, &s_handlerFPE ) == 0;
    ok &= sigaction( SIGILL, &act, &s_handlerILL ) == 0;
    ok &= sigaction( SIGBUS, &act, &s_handlerBUS ) == 0;
    ok &= sigaction( SIGSEGV, &act, &s_handlerSEGV ) == 0;
    if( !ok ) {
      wxLogDebug( _T( "无法安装我们的信号处理程序." ) );
    }
    s_savedHandlers = true;
  } else if( s_savedHandlers ) {
    // uninstall the signal handler
    ok &= sigaction( SIGFPE, &s_handlerFPE, NULL ) == 0;
    ok &= sigaction( SIGILL, &s_handlerILL, NULL ) == 0;
    ok &= sigaction( SIGBUS, &s_handlerBUS, NULL ) == 0;
    ok &= sigaction( SIGSEGV, &s_handlerSEGV, NULL ) == 0;
    if( !ok ) {
      wxLogDebug( _T( "无法卸载我们的信号处理程序." ) );
    }
    s_savedHandlers = false;
  }
  //else: nothing to do
  return ok;
}

#endif // wxUSE_ON_FATAL_EXCEPTION

#endif // wxUSE_BASE

#ifdef __DARWIN__
#include <sys/errno.h>
#endif
#if wxUSE_GUI

#define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__))

#if !USE_OLD_DARWIN_END_PROCESS_DETECT

bool wxGUIAppTraits::CreateEndProcessPipe( wxExecuteData& execData ) {
  return execData.pipeEndProcDetect.Create();
}

bool wxGUIAppTraits::IsWriteFDOfEndProcessPipe( wxExecuteData& execData, int fd ) {
  return fd == ( execData.pipeEndProcDetect )[wxPipe::Write];
}

void wxGUIAppTraits::DetachWriteFDOfEndProcessPipe( wxExecuteData& execData ) {
  execData.pipeEndProcDetect.Detach( wxPipe::Write );
  execData.pipeEndProcDetect.Close();
}

#else // !Darwin

bool wxGUIAppTraits::CreateEndProcessPipe( wxExecuteData & ( execData ) ) {
  return true;
}

bool
wxGUIAppTraits::IsWriteFDOfEndProcessPipe( wxExecuteData & ( execData ),
    int ( fd ) ) {
  return false;
}

void
wxGUIAppTraits::DetachWriteFDOfEndProcessPipe( wxExecuteData & ( execData ) ) {
  // nothing to do here, we don't use the pipe
}

#endif // !Darwin/Darwin

int wxGUIAppTraits::WaitForChild( wxExecuteData& execData ) {
  wxEndProcessData *endProcData = new wxEndProcessData;
  const int flags = execData.flags;
  if( flags & wxEXEC_SYNC ) {
    endProcData->process = NULL;
    endProcData->pid = -execData.pid;
  } else {
    endProcData->process  = execData.process;
    endProcData->pid      = execData.pid;
  }
  #if USE_OLD_DARWIN_END_PROCESS_DETECT
  endProcData->tag = wxAddProcessCallbackForPid( endProcData, execData.pid );
  #else
  endProcData->tag = wxAddProcessCallback
                     (
                       endProcData,
                       execData.pipeEndProcDetect.Detach( wxPipe::Read )
                     );
  execData.pipeEndProcDetect.Close();
  #endif // USE_OLD_DARWIN_END_PROCESS_DETECT
  if( flags & wxEXEC_SYNC ) {
    wxBusyCursor bc;
    wxWindowDisabler *wd = flags & wxEXEC_NODISABLE ? NULL
                           : new wxWindowDisabler;
    // endProcData->pid will be set to 0 from GTK_EndProcessDetector when the
    // process terminates
    while( endProcData->pid != 0 ) {
      bool idle = true;
      #if HAS_PIPE_INPUT_STREAM
      if( execData.bufOut ) {
        execData.bufOut->Update();
        idle = false;
      }
      if( execData.bufErr ) {
        execData.bufErr->Update();
        idle = false;
      }
      #endif // HAS_PIPE_INPUT_STREAM
      if( idle ) {
        wxMilliSleep( 1 );
      }
      wxYield();
    }
    int exitcode = endProcData->exitcode;
    delete wd;
    delete endProcData;
    return exitcode;
  } else {
    return execData.pid;
  }
}
#endif

#if wxUSE_BASE

void wxHandleProcessTermination( wxEndProcessData *proc_data ) {
  if( proc_data->process ) {
    proc_data->process->OnTerminate( proc_data->pid, proc_data->exitcode );
  }
  if( proc_data->pid > 0 ) {
    delete proc_data;
  } else {
    proc_data->pid = 0;
  }
}

#endif
